import { rewardCalculationService } from './rewardCalculationService'
import { rewardDistributionStatusService } from './rewardDistributionStatusService'
import { DAILY_DISTRIBUTION_COUNT, DISTRIBUTION_INTERVAL_HOURS } from '../constants/reward'
import { addBeijingHours, formatBeijingTime, getBeijingStartOfDay } from '../utils/timezone'

export class CronService {
  private intervals: Map<string, NodeJS.Timeout> = new Map()
  private isInitialized = false
  private rewardDistributionRunning = false
  private rewardDistributionPending = false

  private formatLogTime(date: Date = new Date()): string {
    return formatBeijingTime(date)
  }

  /**
   * 初始化定时任务
   */
  init(): void {
    if (this.isInitialized) {
      console.log('정기 작업이 이미 초기화되어 다시 초기화하지 않습니다.')
      return
    }

    try {
      console.log('정기 작업 초기화를 시작합니다...')

      // 每天凌晨0点计算当日收益
      this.startDailyRewardCalculationTask()

      // 每2小时发放收益
      this.startRewardDistributionTask()

      // 每日凌晨3点进行深度收益计算（保留作为备份）
      this.startDailyMaintenanceTask()

      this.isInitialized = true
      console.log('정기 작업 초기화가 완료되었습니다.')

    } catch (error) {
      console.error('정기 작업 초기화 실패:', error)
    }
  }

  /**
   * 【新】启动每日收益计算任务 - 每天凌晨0点执行
   */
  private startDailyRewardCalculationTask(): void {
    const taskName = 'daily_reward_calculation'

    // 先清理可能存在的旧任务
    this.stopTask(taskName)

    // ✅ 修复：立即执行一次收益计算，然后触发首次发放
    this.executeDailyRewardCalculation().then(() => {
      console.log('✅ 收益计算完成，2秒后触发首次发放...')
      // 等待2秒让数据库写入完成，然后触发首次发放
      setTimeout(() => {
        void this.runRewardDistributionCycle()
      }, 2000)
    })

    // 计算到下一个凌晨0点的时间
    const now = new Date()
    const todayStart = getBeijingStartOfDay(now)
    const nextMidnight = addBeijingHours(todayStart, 24)
    const timeToNextMidnight = Math.max(nextMidnight.getTime() - now.getTime(), 0)

    // 设置首次执行的延迟
    setTimeout(() => {
      this.executeDailyRewardCalculation()

      // 设置每日重复执行
      const dailyInterval = setInterval(async () => {
        await this.executeDailyRewardCalculation()
      }, 24 * 60 * 60 * 1000) // 24小时

      this.intervals.set(taskName, dailyInterval)
    }, timeToNextMidnight)

    console.log(`🔄 每日收益计算任务已启动 (立即初始化 + 每天北京时间0点执行)`)
    console.log(`📅 下次定时执行时间: ${formatBeijingTime(nextMidnight)}`)
  }

  /**
   * 【新】启动收益发放任务 - 每2小时执行一次
   */
  private startRewardDistributionTask(): void {
    const taskName = 'reward_distribution'

    // 先清理可能存在的旧任务
    this.stopTask(taskName)

    console.log('🔄 收益发放任务已启动 (依赖数据库分发进度)')

    void this.runRewardDistributionCycle()
  }

  /**
   * 按照数据库记录的发放进度执行收益发放，并安排下一次执行时间
   */
  private async runRewardDistributionCycle(): Promise<void> {
    const taskName = 'reward_distribution'

    if (this.rewardDistributionRunning) {
      console.log('ℹ️ 收益发放正在执行中，标记完成后立即再检查。')
      this.rewardDistributionPending = true
      return
    }

    this.rewardDistributionRunning = true

    // 如果已有定时器在等待，先清理，避免重复执行
    const existingTimer = this.intervals.get(taskName)
    if (existingTimer) {
      clearTimeout(existingTimer)
      this.intervals.delete(taskName)
    }

    try {
      const now = new Date()
      const todayStart = rewardDistributionStatusService.getStartOfDay(now)
      const slotIntervalMs = DISTRIBUTION_INTERVAL_HOURS * 60 * 60 * 1000

      let status = await rewardDistributionStatusService.getTodayStatus(now)

      const elapsedMs = Math.max(now.getTime() - todayStart.getTime(), 0)
      let expectedSlotCount = 0
      if (elapsedMs >= 0) {
        expectedSlotCount = Math.min(
          Math.floor(elapsedMs / slotIntervalMs) + 1,
          DAILY_DISTRIBUTION_COUNT
        )
      }

      if (expectedSlotCount < status.distributionCount) {
        expectedSlotCount = status.distributionCount
      }

      while (status.distributionCount < expectedSlotCount && status.distributionCount < DAILY_DISTRIBUTION_COUNT) {
        const slotIndex = status.distributionCount
        const scheduledTime = addBeijingHours(todayStart, slotIndex * DISTRIBUTION_INTERVAL_HOURS)

        console.log(
          `⏱️ 槽位 #${slotIndex + 1} (计划时间 ${formatBeijingTime(scheduledTime)}) 已到，立即执行收益发放。`
        )

        try {
          const processed = await this.executeRewardDistribution(scheduledTime, slotIndex)
          status = await rewardDistributionStatusService.markSlotProcessed(scheduledTime)
          console.log(
            `📌 今日第 ${status.distributionCount}/${DAILY_DISTRIBUTION_COUNT} 次发放记录完成，本次处理用户数: ${processed}`
          )
        } catch (error) {
          console.error('❌ 收益发放执行失败，将在5分钟后重试。原因:', error)

          const retryTimeout = setTimeout(() => {
            void this.runRewardDistributionCycle()
          }, 5 * 60 * 1000)

          this.intervals.set(taskName, retryTimeout)
          return
        }
      }

      if (status.distributionCount >= DAILY_DISTRIBUTION_COUNT) {
        const nextDayStart = addBeijingHours(todayStart, 24)
        console.log(
          `✅ 今日收益发放已完成 (${status.distributionCount}/${DAILY_DISTRIBUTION_COUNT})，` +
          `下一次检查时间: ${formatBeijingTime(nextDayStart)}`
        )

        const delay = Math.max(nextDayStart.getTime() - now.getTime(), 0)
        const timeout = setTimeout(() => {
          void this.runRewardDistributionCycle()
        }, delay)

        this.intervals.set(taskName, timeout)
        return
      }

      const nextSlotTime = addBeijingHours(todayStart, status.distributionCount * DISTRIBUTION_INTERVAL_HOURS)

      if (nextSlotTime.getTime() <= now.getTime()) {
        console.log('ℹ️ 下一槽位计划时间已过，立即再次检查。')
        setTimeout(() => {
          void this.runRewardDistributionCycle()
        }, 0)
        return
      }

      console.log(`⏰ 下一次收益发放计划时间: ${formatBeijingTime(nextSlotTime)}`)

      const delay = Math.max(nextSlotTime.getTime() - now.getTime(), 0)
      const timeout = setTimeout(() => {
        void this.runRewardDistributionCycle()
      }, delay)

      this.intervals.set(taskName, timeout)

    } catch (error) {
      console.error('收益发放调度循环异常，将在5分钟后重试:', error)

      const timeout = setTimeout(() => {
        void this.runRewardDistributionCycle()
      }, 5 * 60 * 1000)

      this.intervals.set(taskName, timeout)
    } finally {
      this.rewardDistributionRunning = false

      if (this.rewardDistributionPending) {
        this.rewardDistributionPending = false
        setTimeout(() => {
          void this.runRewardDistributionCycle()
        }, 0)
      }
    }
  }

  /**
   * 执行每日收益计算
   */
  private async executeDailyRewardCalculation(): Promise<void> {
    try {
      console.log(`[${this.formatLogTime()}] 开始执行每日收益计算...`)
      await rewardCalculationService.calculateDailyRewards()
      await this.ensurePreviousDayCompleted()
      console.log(`[${this.formatLogTime()}] 每日收益计算完成`)

      console.log('✅ 收益计算完成，2秒后刷新收益发放调度...')
      setTimeout(() => {
        void this.runRewardDistributionCycle()
      }, 2000)
    } catch (error) {
      console.error(`[${this.formatLogTime()}] 每日收益计算失败:`, error)
    }
  }

  private async ensurePreviousDayCompleted(): Promise<void> {
    const now = new Date()
    const todayStart = rewardDistributionStatusService.getStartOfDay(now)
    const previousDay = new Date(todayStart)
    previousDay.setUTCDate(previousDay.getUTCDate() - 1)

    try {
      const status = await rewardDistributionStatusService.getOrCreateStatusForDate(previousDay)
      const dateLabel = formatBeijingTime(previousDay)

      if (status.distributionCount >= DAILY_DISTRIBUTION_COUNT) {
        console.log(`🧾 ${dateLabel} 的收益槽位记录为 ${status.distributionCount}/${DAILY_DISTRIBUTION_COUNT}，无需额外补发。`)
        return
      }

      console.warn(
        `⚠️ 检测到 ${dateLabel} 仅记录 ${status.distributionCount}/${DAILY_DISTRIBUTION_COUNT} 次发放，` +
        `正在对所有用户执行补发以确保满 12 次。`
      )

      const result = await rewardCalculationService.backfillAllUsersForDate(previousDay)

      if (result.processedUsers > 0) {
        console.log(
          `🔁 ${dateLabel} 补发完成：${result.processedUsers} 位用户，共 ${result.totalInstallments} 次，` +
          `累计 ${result.totalRewardNear.toFixed(4)} NEAR`
        )
      } else {
        console.log(`ℹ️ ${dateLabel} 无需补发，可能已由其他流程补齐。`)
      }
    } catch (error) {
      console.error('❌ 检查/补发上一自然日收益时发生错误:', error)
    }
  }

  /**
   * 执行收益发放
   */
  private async executeRewardDistribution(executionTime: Date, slotIndex: number): Promise<number> {
    const scheduledAt = executionTime

    try {
      console.log(
        `[${this.formatLogTime()}] 开始执行收益发放 (槽位 #${slotIndex + 1}, 计划时间: ${formatBeijingTime(scheduledAt)})...`
      )

      const processed = await rewardCalculationService.distributeRewards(scheduledAt, slotIndex)

      console.log(
        `[${this.formatLogTime()}] 收益发放完成，本次处理用户: ${processed}`
      )

      return processed
    } catch (error) {
      console.error(`[${this.formatLogTime()}] 收益发放失败:`, error)
      throw error
    }
  }

  /**
   * 启动收益更新任务 - 每小时执行
   */
  private startRewardUpdateTask(): void {
    const taskName = 'reward_update'

    // 先清理可能存在的旧任务
    this.stopTask(taskName)

    // 立即执行一次
    this.executeRewardUpdate()

    // 设置定时任务 - 每小时执行一次
    const interval = setInterval(async () => {
      await this.executeRewardUpdate()
    }, 60 * 60 * 1000) // 1小时

    this.intervals.set(taskName, interval)
    console.log('수익 업데이트 정기 작업이 시작되었습니다 (매시간 실행).')
  }

  /**
   * 启动每日维护任务 - 每天凌晨3点执行
   */
  private startDailyMaintenanceTask(): void {
    const taskName = 'daily_maintenance'

    // 先清理可能存在的旧任务
    this.stopTask(taskName)

    // 计算到下一个北京时间凌晨3点的时间
    const now = new Date()
    const todayStart = getBeijingStartOfDay(now)
    let next3AM = addBeijingHours(todayStart, 3)

    if (now > next3AM) {
      next3AM = addBeijingHours(todayStart, 27)
    }

    const timeToNext3AM = Math.max(next3AM.getTime() - now.getTime(), 0)

    // 设置首次执行的延迟
    setTimeout(() => {
      this.executeDailyMaintenance()

      // 设置每日重复执行
      const dailyInterval = setInterval(async () => {
        await this.executeDailyMaintenance()
      }, 24 * 60 * 60 * 1000) // 24小时

      this.intervals.set(taskName, dailyInterval)
    }, timeToNext3AM)

    console.log(`일일 유지 보수 작업이 예약되었습니다. 최초 실행: ${formatBeijingTime(next3AM)}`)
  }

  /**
   * 执行收益更新
   */
  private async executeRewardUpdate(): Promise<void> {
    try {
      console.log(`[${this.formatLogTime()}] 수익 업데이트 작업을 시작합니다.`)

      await rewardCalculationService.updateAllStakingRewards()

      console.log(`[${this.formatLogTime()}] 수익 업데이트 작업이 완료되었습니다.`)
    } catch (error) {
      console.error(`[${this.formatLogTime()}] 수익 업데이트 작업 실패:`, error)
    }
  }

  /**
   * 执行每日维护
   */
  private async executeDailyMaintenance(): Promise<void> {
    try {
      console.log(`[${this.formatLogTime()}] 일일 유지 보수 작업을 시작합니다.`)

      // 深度收益计算
      await rewardCalculationService.updateAllStakingRewards()

      // 这里可以添加其他维护任务
      // 例如：清理过期数据、生成报告等

      console.log(`[${this.formatLogTime()}] 일일 유지 보수 작업이 완료되었습니다.`)
    } catch (error) {
      console.error(`[${this.formatLogTime()}] 일일 유지 보수 작업 실패:`, error)
    }
  }

  /**
   * 停止指定任务
   */
  stopTask(taskName: string): void {
    const interval = this.intervals.get(taskName)
    if (interval) {
      clearInterval(interval)
      this.intervals.delete(taskName)
      console.log(`정기 작업 ${taskName}이(가) 중지되었습니다.`)
    }
  }

  /**
   * 停止所有任务
   */
  stopAllTasks(): void {
    for (const [taskName, interval] of this.intervals) {
      clearInterval(interval)
      console.log(`정기 작업 ${taskName}이(가) 중지되었습니다.`)
    }
    this.intervals.clear()
    this.isInitialized = false
    console.log('모든 정기 작업이 중지되었습니다.')
  }

  /**
   * 수동으로 수익 업데이트를 실행합니다. (테스트용)
   */
  async manualTriggerRewardUpdate(): Promise<string> {
    try {
      console.log('수동으로 수익 업데이트를 실행합니다.')
      await this.executeRewardUpdate()
      return '수익 업데이트가 완료되었습니다.'
    } catch (error) {
      console.error('수동 수익 업데이트 실패:', error)
      const errorMessage = error instanceof Error ? error.message : '알 수 없는 오류가 발생했습니다.'
      return `수익 업데이트 실패: ${errorMessage}`
    }
  }

  /**
   * 获取任务상태
   */
  getTaskStatus(): Array<{
    name: string
    isRunning: boolean
  }> {
    const tasks = ['reward_update', 'daily_maintenance']

    return tasks.map(taskName => ({
      name: taskName,
      isRunning: this.intervals.has(taskName)
    }))
  }

  /**
   * 重启所有任务
   */
  restart(): void {
    console.log('정기 작업 서비스를 다시 시작합니다.')
    this.stopAllTasks()
    this.init()
  }
}

// 创建全局实例
export const cronService = new CronService()
